﻿using System.Reflection;
using System.Xml;

namespace ConsoleApp1
{
    /// <summary>
    /// 该类读取XML并动态创建在XML中声明的所有类对象，其中如何创建由开发者自定义
    /// </summary>
    internal class XMLClassLoader
    {
        /// <summary>
        /// 对象池
        /// </summary>
        private static Dictionary<string, object> objects = new Dictionary<string, object>();

        public static void LoadClasses()
        {
            XmlDocument doc = new XmlDocument();
            XmlReaderSettings settings = new XmlReaderSettings();
            settings.IgnoreComments = true;
            XmlReader reader = XmlReader.Create(@"LoadClass.xml", settings);
            doc.Load(reader);

            XmlNode classesNode = doc.SelectSingleNode("classes")!;
            XmlNodeList classes = classesNode!.ChildNodes;
            foreach (XmlNode node in classes)
            {
                XmlElement _class = node as XmlElement;
                //提取类名
                XmlNode name = _class!.SelectSingleNode("name")!;
                //Console.WriteLine($"class name:{name?.InnerText}");
                string className = name.InnerText;
                //提取该类构造时所需参数
                XmlNode argsNode = _class.SelectSingleNode("arguments")!;
                XmlNodeList args = argsNode.ChildNodes;
                List<Argument> arguments = new List<Argument>();
                foreach (XmlNode arg in args)
                {
                    XmlElement _arg = arg as XmlElement;
                    string type = _arg.GetAttribute("type").Trim();
                    string _idx = _arg.GetAttribute("index").Trim();
                    string _ref = _arg.GetAttribute("ref").Trim();
                    string _val1 = _arg.GetAttribute("value").Trim();
                    string _val = string.IsNullOrEmpty(_val1)? _arg.InnerText.Trim() : _val1;
                    //Console.WriteLine($"args:{_val} type:{_type} index:{idx} ref:{_ref}");
                    arguments.Add(new Argument()
                    {
                        Type = type,
                        Value = _val,
                        Index = Convert.ToInt32(_idx),
                        Ref = _ref
                    });
                }
                //提取别名
                XmlNode objectref = _class.SelectSingleNode("objectref")!;
                //Console.WriteLine($"object ref:{objectref.InnerText}");
                string objref = objectref.InnerText;
                //开始创建对象
                try
                {
                    Type t = Type.GetType(className)!;
                    Assembly asm = t.Assembly;
                    object[] argsArray = new object[arguments.Count];
                    //解析出所有参数并加入到object列表
                    //（demo程序所以仅考虑所有参数都是正常输入且不漏输attribute或value的情况）
                    foreach (Argument argu in arguments)
                    {
                        if (!string.IsNullOrEmpty( argu.Ref))      //如果当前参数有ref，先处理ref
                        {
                            if (objects.ContainsKey(argu.Ref))
                            {
                                object obj = objects[argu.Ref];
                                if (obj.GetType().FullName == argu.Type)  //如果当前对象池中存在该ref对象，并且与xml中声明的形参类型相同
                                {
                                    argsArray[argu.Index] = obj;
                                }
                            }
                        }
                        else
                        {
                            Type _t = Type.GetType(argu.Type)!;  
                            var res = ConvertTo(_t, argu.Value)!;   //构造指定类型的实参
                            argsArray[argu.Index] = res;
                        }
                    }
                    //用反射调用构造函数创建对象
                    var inst = asm.CreateInstance(className!, true,
                        BindingFlags.Default, null, argsArray, null, null);
                    //加入到字典
                    objects[objref] = inst!;
                }
                catch (Exception)
                {

                    throw;
                }
            }
        }

        public static T? GetInstance<T>(string objectRef) where T : class
        {
            T? res = default(T?);

            if (objects.ContainsKey(objectRef))
            {
                res = objects[objectRef] as T;
            }

            return res;
        }

        public static void DestroyInstance(string objectRef)
        {
            if (objects.ContainsKey(objectRef))
            {
                var obj = objects[objectRef];
                objects.Remove(objectRef);
                obj = null;
            }
        }

        public static void DestroyAll()
        {
            objects.Clear();
        }

        private static object? ConvertTo(Type type,string value)
        {
            object? res = default(object?);
            if (type.Name == "String")
            {
                return value;
            }
            if (type.IsValueType)
            {
                if (type.Name == "Int16" || type.Name == "Int32" || type.Name == "Int64" || /*type.Name == "Int128" || 暂不支持.NET7以上 */
                type.Name == "Boolean" || type.Name == "Byte" || type.Name == "Char" ||
                type.Name == "Decimal" || type.Name == "DateTime" || type.Name == "Double" ||
                type.Name == "Single")
                {
                    var methodInfo = type.GetMethod("Parse", BindingFlags.Static | BindingFlags.Public,
                        null, CallingConventions.Any, new Type[] { typeof(string) }, null);
                    try
                    {
                        res = methodInfo?.Invoke(null, new object[] { value });
                    }
                    catch (Exception)
                    {
                        throw;
                    }
                }
            }
            return res;
        }
    }

    struct Argument
    {
        internal string Type;
        internal string Value;
        internal int Index;
        internal string Ref;
    }
}
